// =====================================================================================
// 
//       Filename:  raster.cpp
// 
//    Description:  Implémentation d'une table de données raster
// 
//        Version:  1.0
//        Created:  24/07/2009 00:27:10
//       Revision:  none
//       Compiler:  g++
// 
//         Author:  François Hissel (fh), francois.hissel@m4x.org
//        Company:  
// 
// =====================================================================================

#include	<string>
#include	<fstream>
#include	<cstdlib>
#include	<limits>
#include	<cfloat>
#include	"parser.h"
#include	"raster.h"

const RGB NODATA=RGB(0.5,0.5,0.5);

/***********************************************/
/* IMPLEMENTATION DE LA CLASSE RASTEREXCEPTION */
/***********************************************/
const char* RasterException::what() const throw() {
	switch (code) {
		case BAD_INDEX:return ("Erreur d'indice : "+message).c_str();
		case BAD_DEFINITION:return ("Erreur de définition de classe : "+message).c_str();
		default:return message.c_str();
	}
}

/***********************************/
/* IMPLEMENTATION DE LA CLASSE RGB */
/***********************************/
RGB RGB::couleur(double valeur) {
	if (valeur<0 || valeur>1) return NODATA;
	valeur*=2;
	if (valeur<1) return RGB(1,1,1-valeur);
	else return RGB(1,2-valeur,0);
}

RGB RGB::palette(unsigned char num) {
	switch (num) {
		case 0: return RGB(0,0,0);
		case 1: return RGB(0,0,1);
		case 2: return RGB(0,1,0);
		case 3: return RGB(0,0.5,0.5);
		case 4: return RGB(1,0,0);
		case 5: return RGB(0.5,0,0.5);
		case 6: return RGB(0.5,0.5,0);
		case 7: return RGB(0.33,0.33,0.33);
		case 8: return RGB(0.66,0.66,0.66);
		case 9: return RGB(0.5,0.5,1);
		case 10: return RGB(0.5,1,0.5);
		case 11: return RGB(0,1,1);
		case 12: return RGB(1,0.5,0.5);
		case 13: return RGB(1,0,1);
		case 14: return RGB(1,1,0);
		case 15: return RGB(1,1,1);
		default: return NODATA;
	}
}

int RGB::to_int() const {
	int rr=(int)(r*0xFF);
	int gg=(int)(g*0xFF);
	int bb=(int)(b*0xFF);
	return 0x00010000*rr+0x00000100*gg+0x00000001*bb;
}

/**************************************/
/* IMPLEMENTATION DE LA CLASSE RASTER */
/**************************************/
Raster::Raster(Type ptype,int ptaillex,int ptailley,double pxmin,double pymin,double pcellule):_type(ptype),_taillex(ptaillex),_tailley(ptailley),_xmin(pxmin),_ymin(pymin),_cellule(pcellule),_novalue(DBL_MAX) {
	if (ptaillex<0 || ptailley<0 || pcellule<0) throw RasterException(RasterException::BAD_DEFINITION,"Raster");
}

void Raster::exporte(string fichier) const {
	ofstream ofs(fichier.c_str(),ofstream::out);
	ofs << "ncols " << _taillex << "\n";
	ofs << "nrows " << _tailley << "\n";
	ofs << "xllcorner " << _xmin << "\n";
	ofs << "yllcorner " << _ymin << "\n";
	ofs << "cellsize " << _cellule << "\n";
	for (int j=_tailley-1;j>=0;--j) {
		for (int i=0;i<_taillex;++i) {
			affiche(i,j,ofs);
			ofs << " ";
		}
		ofs << "\n";
	}
	ofs.close();
}

/*********************************************/
/* IMPLEMENTATION DE LA CLASSE RASTERCONTINU */
/*********************************************/
RasterContinu::RasterContinu(int ptaillex,int ptailley,double pxmin,double pymin,double pcellule,double pmin,double pmax):Raster(CONTINU,ptaillex,ptailley,pxmin,pymin,pcellule),min(pmin),max(pmax) {
	if (max<min) throw RasterException(RasterException::BAD_DEFINITION,"RasterContinu");
	_data=new double*[ptaillex];
	for (int i=0;i<ptaillex;++i) _data[i]=new double[ptailley];
}

RasterContinu::~RasterContinu() {
	for (int i=0;i<_taillex;++i) delete[] _data[i];
	delete[] _data;
}

RGB RasterContinu::couleur(int i,int j) const {
	if (_data[i][j]==_novalue) return NODATA;
	double coul=(_data[i][j]-min)/(max-min);
	if (coul>1) coul=1;
	return RGB::couleur(coul);
}

void RasterContinu::auto_bornes() {
	min=DBL_MAX;max=-DBL_MAX;
	for (int i=0;i<_taillex;++i) {
		for (int j=0;j<_tailley;++j) {
			if (_data[i][j]!=_novalue) {
				if (min>_data[i][j]) min=_data[i][j];
				if (max<_data[i][j]) max=_data[i][j];
			}
		}
	}
}

void RasterContinu::_importe(string fichier,bool auto_zone,double pxmin,double pymin,double pxmax,double pymax) {
	ifstream ifs(fichier.c_str(),ifstream::in);
	ifs.exceptions(ifstream::badbit);
	bool debut=false;
	string line;
	size_t pos;
	while (!debut) {
		pos=0;
		string nom;
		getline(ifs,line);
		size_t i=0;
		while (i<line.size() && line[i]==' ') ++i;
		if (i<line.size() && line[i]>='0' && line[i]<='9') debut=true;	// Teste si on a commencé à décrire les données
		if (!debut) {	// Sinon, on lit une variable et on l'interprète
			nom=getstring(line,pos,' ');
			if (string_compare(nom,"ncols")) _taillex=getint(line,pos,' ');
			else if (string_compare(nom,"nrows")) _tailley=getint(line,pos,' ');
			else if (string_compare(nom,"xllcorner")) _xmin=getfloat(line,pos,' ');
			else if (string_compare(nom,"yllcorner")) _ymin=getfloat(line,pos,' ');
			else if (string_compare(nom,"cellsize")) _cellule=getfloat(line,pos,' ');
			else if (string_compare(nom,"nodata_value")) _novalue=getfloat(line,pos,' ');
		}
	}
	// Réajustement des variables en fonction du cadre imposé
	int offsetxl=0;	// Nombre de colonnes à ignorer à gauche
	int offsetyt=0;	// Nombre de lignes à ignorer en haut
	int offsetxr=0;	// Nombre de colonnes à ignorer à droite
	int offsetyb=0;	// Nombre de lignes à ignorer en bas
	if (!auto_zone) {
		double _xmax=_xmin+_cellule*_taillex;
		double _ymax=_ymin+_cellule*_tailley;
		if (pxmin>_xmin) {
			offsetxl=(int)((pxmin-_xmin)/_cellule);
			pxmin=_xmin+offsetxl*_cellule;
		} else pxmin=_xmin;
		if (pxmax<_xmax) {
			offsetxr=(int)((_xmax-pxmax)/_cellule);
			pxmax=_xmax-offsetxr*_cellule;
		} else pxmax=_xmax;
		if (pymin>_ymin) {
			offsetyb=(int)((pymin-_ymin)/_cellule);
			pymin=_ymin+offsetyb*_cellule;
		} else pymin=_ymin;
		if (pymax<_ymax) {
			offsetyt=(int)((_ymax-pymax)/_cellule);
			pymax=_ymax-_cellule*offsetyt;
		} else pymax=_ymax;
		_xmin=pxmin;_ymin=pymin;
		_taillex-=(offsetxl+offsetxr);
		_tailley-=(offsetyt+offsetyb);
	}
	// Création des structures de données
	_data=new double*[_taillex];
	for (int i=0;i<_taillex;++i) _data[i]=new double[_tailley];
	// Boucle de lecture des données, la première ligne est déjà en mémoire dans la variable line
	for (int k=0;k<offsetyt;++k) ifs.ignore(numeric_limits<streamsize>::max(),'\n');
	for (int j=_tailley-1;j>=0;--j) {
		for (int k=0;k<offsetxl;++k) getfloat(line,pos,' ');
		for (int i=0;i<_taillex;++i) {
			_data[i][j]=getfloat(line,pos,' ');
		}
		if (j>=0) getline(ifs,line);
		pos=0;
	}
	ifs.close();
}

/*********************************************/
/* IMPLEMENTATION DE LA CLASSE RASTERDISCRET */
/*********************************************/
RasterDiscret::RasterDiscret(int pclasses,int ptaillex,int ptailley,double pxmin,double pymin,double pcellule):Raster(DISCRET,ptaillex,ptailley,pxmin,pymin,pcellule),_classes(pclasses) {
	if (pclasses<=0) throw RasterException(RasterException::BAD_DEFINITION,"RasterDiscret");
	_data=new int*[ptaillex];
	for (int i=0;i<ptaillex;++i) _data[i]=new int[ptailley];
	_couleurs=new RGB[pclasses];
	for (int i=0;i<pclasses;++i) {
		_couleurs[i].r=0.5+(double(rand())/RAND_MAX*0.4);
		_couleurs[i].g=0.5+(double(rand())/RAND_MAX*0.4);
		_couleurs[i].b=0.5+(double(rand())/RAND_MAX*0.4);
	}
}

RasterDiscret::~RasterDiscret() {
	for (int i=0;i<_taillex;++i) delete[] _data[i];
	delete[] _data;
	delete[] _couleurs;
}

RGB RasterDiscret::couleur(int i,int j) const {
	if (_data[i][j]>=0 && _data[i][j]<_classes) return _couleurs[_data[i][j]];
	return NODATA;
}
